package org.locker.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*;
import cn.hutool.json.JSONUtil;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingReceiver;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingRequest;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.locker.common.core.exception.ServiceException;
import org.locker.common.core.utils.MapstructUtils;
import org.locker.common.core.utils.StringUtils;
import org.locker.common.mybatis.core.domain.BaseEntity;
import org.locker.common.mybatis.core.page.PageQuery;
import org.locker.common.oss.constant.OssConstant;
import org.locker.common.satoken.utils.LoginHelper;
import org.locker.config.WxPayProperties;
import org.locker.device.dto.ManagerClientQueryDto;
import org.locker.domain.*;
import org.locker.domain.bo.OrderBo;
import org.locker.domain.constants.Constants;
import org.locker.domain.enums.*;
import org.locker.domain.vo.*;
import org.locker.mapper.OrderMapper;
import org.locker.netty.MachineService;
import org.locker.service.*;
import org.locker.system.service.ISysConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Year;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.locker.common.oss.constant.OssConstant.SYS_FREE_MINUTES_KEY;

/**
 * 订单Service业务层处理
 *
 * @author winnie
 * @date 2023-10-11
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    private final OrderMapper baseMapper;
    private final IDeviceService deviceService;
    private final ISysConfigService sysConfigService;
    private final IDeviceGridService deviceGridService;
    private final IUserService userService;
    private final IDeviceOperateLogService deviceOperateLogService;
    private final IManagerConfigService managerConfigService;
    private final IDividendRecordService dividendRecordService;
    private final IManagerFeesService feesService;
    private final IPaySplitService iPaySplitService;
    private final IPositionService positionService;
    private final WxPayService wxPayService;
    private final WxPayProperties properties;
    private final IOrderStatisticsService orderStatisticsService;

    @Autowired
    private ScheduledExecutorService scheduledExecutorService;

    @Lazy()
    @Resource
    private MachineService machineService;

    @Lazy()
    @Resource
    private IWithdrawService withdrawService;

    private static final int ORDER_IN_USED_LIMIT = 5;
    private static final int ORDER_SPILT_RATE = 30;

    /**
     * 查询订单
     */
    @Override
    public OrderVo queryById(Long orderId) {
        OrderVo orderVo = baseMapper.getById(orderId);
        if (ObjectUtil.isNull(orderVo)) {
            return null;
        }
        List<DeviceOperateLog> list = deviceOperateLogService.list(Wrappers.<DeviceOperateLog>lambdaQuery()
            .eq(DeviceOperateLog::getOrderNo, orderVo.getOrderNo()).orderByDesc(BaseEntity::getCreateTime));
        orderVo.setDeviceOperateLogList(list);
        return orderVo;
    }

    /**
     * 查询订单列表
     */
    @Override
    public Page<OrderVo> queryPageList(OrderBo bo, PageQuery pageQuery) {
        return baseMapper.list(pageQuery.build(), bo);
    }

    /**
     * 查询订单列表
     */
    @Override
    public List<OrderVo> queryList(OrderBo bo) {
        LambdaQueryWrapper<Order> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<Order> buildQueryWrapper(OrderBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<Order> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getUserId() != null, Order::getUserId, bo.getUserId());
        lqw.like(StringUtils.isNotBlank(bo.getPhone()), Order::getPhone, bo.getPhone());
        lqw.like(StringUtils.isNotBlank(bo.getDeviceNo()), Order::getDeviceNo, bo.getDeviceNo());
        lqw.eq(bo.getDeviceGridId() != null, Order::getDeviceGridId, bo.getDeviceGridId());
        lqw.eq(StringUtils.isNotBlank(bo.getOpenPwd()), Order::getOpenPwd, bo.getOpenPwd());
        lqw.like(StringUtils.isNotBlank(bo.getOrderNo()), Order::getOrderNo, bo.getOrderNo());
        lqw.eq(bo.getPositionId() != null, Order::getPositionId, bo.getPositionId());
        lqw.eq(StringUtils.isNotBlank(bo.getTransactionNo()), Order::getTransactionNo, bo.getTransactionNo());
        lqw.eq(bo.getPayAmount() != null, Order::getPayAmount, bo.getPayAmount());
        lqw.eq(bo.getPrestoreDeviceAmount() != null, Order::getPrestoreDeviceAmount, bo.getPrestoreDeviceAmount());
        lqw.eq(bo.getDepositAmount() != null, Order::getDepositAmount, bo.getDepositAmount());
        lqw.eq(bo.getActualDeviceAmount() != null, Order::getActualDeviceAmount, bo.getActualDeviceAmount());
        lqw.eq(bo.getResidueAmount() != null, Order::getResidueAmount, bo.getResidueAmount());
        lqw.eq(bo.getStartTime() != null, Order::getStartTime, bo.getStartTime());
        lqw.eq(bo.getEndTime() != null, Order::getEndTime, bo.getEndTime());
        lqw.eq(StringUtils.isNotBlank(bo.getOrderStatus()), Order::getOrderStatus, bo.getOrderStatus());
        lqw.eq(StringUtils.isNotBlank(bo.getIdNo()), Order::getIdNo, bo.getIdNo());
        lqw.eq(bo.getSuccessTime() != null, Order::getSuccessTime, bo.getSuccessTime());
        lqw.eq(bo.getPayTime() != null, Order::getPayTime, bo.getPayTime());
        lqw.eq(StringUtils.isNotBlank(bo.getIsToBalance()), Order::getIsToBalance, bo.getIsToBalance());
        lqw.eq(StringUtils.isNotBlank(bo.getIsOffline()), Order::getIsOffline, bo.getIsOffline());
        lqw.eq(StringUtils.isNotBlank(bo.getOrderType()), Order::getOrderType, bo.getOrderType());
        lqw.eq(StringUtils.isNotBlank(bo.getPayWay()), Order::getPayWay, bo.getPayWay());
        return lqw;
    }

    /**
     * 新增订单
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Order insertByBo(OrderBo bo) {
        Long userId = LoginHelper.getUserId();
        //判断用户有没有未支付的订单
        long count = this.count(Wrappers.<Order>lambdaQuery().eq(Order::getUserId, userId)
            .eq(Order::getOrderStatus, OrderStatusEnum.WAITING_FOR_PAYMENT.getCode()));
        if (count > 0) {
            throw new ServiceException("您存在未完成的订单，请先支付或者取消待支付的订单！");
        }
        int orderLimit = ORDER_IN_USED_LIMIT;
        String ORDER_LIMIT = sysConfigService.selectConfigByKey(OssConstant.ORDER_IN_USED_LIMIT);
        if (NumberUtil.isNumber(ORDER_LIMIT)) {
            orderLimit = Integer.parseInt(ORDER_LIMIT);
        }
        List<Order> orders = this.list(Wrappers.<Order>lambdaQuery().eq(Order::getUserId, userId)
            .eq(Order::getOrderStatus, OrderStatusEnum.IN_USED.getCode()));
        if (orders.size() >= orderLimit) {
            throw new ServiceException("您已超下单限制，无法继续再下新订单！");
        }
        Set<String> usePasswords = orders.stream().map(a -> a.getOpenPwd()).collect(Collectors.toSet());
        boolean samePwd = this.exists(Wrappers.<Order>lambdaQuery()
            .eq(Order::getOrderStatus, OrderStatusEnum.IN_USED.getCode())
            .eq(Order::getPhone, bo.getPhone())
            .eq(Order::getOpenPwd, bo.getOpenPwd()));
        if (samePwd) {
            throw new ServiceException("您存在相同的用户名和密码订单，请重新设置新密码！");
        }


        Order order = MapstructUtils.convert(bo, Order.class);
//        if (!isValidPhoneNumber(order.getPhone())) {
//            throw new ServiceException("手机号错误！");
//        }
        if (!NumberUtil.isNumber(order.getPhone())) {
            throw new ServiceException("请输入纯数字");
        }
        DeviceAggregatorInfoVo deviceAggregatorInfo = deviceService.getDeviceAggregatorInfo(order.getDeviceNo(), order.getDeviceGridId());
        String deviceStatus = deviceAggregatorInfo.getDevice().getStatus();
        if (!deviceStatus.equals(DeviceStatusEnum.online.getCode())) {
            throw new ServiceException("当前设备离线中，请使用离线方式");
        }

        DeviceGridVo vo = deviceAggregatorInfo.getDeviceGridList().get(0);
        //格子状态
        String gridStatus = vo.getStatus();
        if (!DeviceGridStatusEnum.unlock.getCode().equals(gridStatus)) {
            throw new ServiceException("该格子不能使用！");
        }

        String orderNo = generateOrderNo();
        //计费类型
        String billingType = vo.getBillingType();
        order.setOrderType(billingType);
        order.setOrderNo(orderNo);
        order.setUserId(userId);
        order.setIsToBalance(Constants.NO);
        order.setDepositAmount(vo.getDepositAmount());
        order.setPrestoreDeviceAmount(vo.getMaxAmount());
        order.setRefundStatus(OrderRefundStatusEnum.REFUND_NO.getCode());
        //设置微信需要支付的金额
        order.setPayAmount(vo.getDepositAmount().add(vo.getMaxAmount()));
        order.setPositionId(vo.getPositionId());
        order.setPayWay("wx_pay");
        order.setIsOffline("online");
        order.setUnitPrice(vo.getUnitPriceAmount());
        order.setOrderStatus(OrderStatusEnum.WAITING_FOR_PAYMENT.getCode());
        baseMapper.insert(order);
        //把手机号存入用户信心
        userService.update(Wrappers.<User>lambdaUpdate().set(User::getPhone, order.getPhone()).eq(User::getUserId, userId));

        //把对应的设备格子占用掉
        deviceGridService.locker(order.getDeviceGridId());
        return order;
    }

    @Override
    @Transactional
    public void cancel(Long orderId) {
        Order order = this.getById(orderId);
        if (ObjectUtil.isNull(order) || !OrderStatusEnum.WAITING_FOR_PAYMENT.getCode().equals(order.getOrderStatus())) {
            return;
        }
        this.update(Wrappers.<Order>lambdaUpdate().set(Order::getOrderStatus, OrderStatusEnum.ORDER_CANCELLED.getCode()).eq(Order::getOrderId, orderId));
        //释放格子
        deviceGridService.unlock(order.getDeviceGridId());
    }

    /**
     * 执行订单结束，但是不开锁
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    @Lock4j(expire = 3000, acquireTimeout = 5000)
    public void orderFinished() {
        List<Order> IN_USED_ORDERS = this.list(Wrappers.lambdaQuery(Order.class)
            .eq(Order::getOrderStatus, OrderStatusEnum.IN_USED.getCode()));
        log.info("IN_USED_ORDERS:{}", JSONUtil.toJsonStr(IN_USED_ORDERS));
        for (Order order : IN_USED_ORDERS) {
            close(order, false,true);
        }

        log.info("====================开始释放所有格子====================");

        //
        deviceGridService.update(Wrappers.lambdaUpdate(DeviceGrid.class)
            .eq(DeviceGrid::getGridType, "common")
            .eq(DeviceGrid::getStatus, DeviceGridStatusEnum.locker.getCode())
            .set(DeviceGrid::getStatus, DeviceGridStatusEnum.unlock.getCode())
        );

    }

    @Override
    @Lock4j(keys = {"#order.orderNo"}, expire = 3000, acquireTimeout = 5000)
    @Transactional(rollbackFor = Exception.class)
    public void close(Order order, boolean needOpen,boolean isAutoClean) {
        log.info("close==> {},needOpen {}", JSONUtil.toJsonStr(order), needOpen);
        if (ObjectUtil.isNull(order) || !OrderStatusEnum.IN_USED.getCode().equals(order.getOrderStatus())) {
            return;
        }
        //计算订单金额，将押金与剩余预付金转移到用户余额
        //结束时间
        DateTime date = DateUtil.date();
        BigDecimal fee = calculateFeeByHours(order, DateUtil.toLocalDateTime(date));
        log.info("close=========>fee  {}", fee);
        //余额
        BigDecimal subtract = order.getPayAmount().subtract(fee);
        this.update(Wrappers.<Order>lambdaUpdate()
            .set(Order::getResidueAmount, subtract)
            .set(Order::getActualDeviceAmount, fee)
            .set(Order::getEndTime, date)
            .set(Order::getOrderStatus, OrderStatusEnum.USED_DONE.getCode())
            .set(fee.compareTo(BigDecimal.ZERO) == 1, Order::getSettlementStatus, OrderSettlementStatusEnum.SETTLEABLE.getCode())
            .eq(Order::getOrderId, order.getOrderId()));


        //将余额写入用户账户
        if (BigDecimal.ZERO.compareTo(subtract) == -1) {
            if (order.getUserId() != null) {
                userService.update(Wrappers.<User>lambdaUpdate()
                    .set(User::getBalanceAmount, subtract)
                    .set(User::getLatestOrderNo, order.getOrderNo())
                    .eq(User::getUserId, order.getUserId()));
            }
        }
        log.info("close=========>fee ====>pushOrderFinish {}", order.getOrderNo());
        //释放格子 同步信息给到设备
        machineService.pushOrderFinish(order.getOrderNo(), needOpen);


        deviceGridService.unlock(order.getDeviceGridId());

        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                TimerTask task = new TimerTask() {
                    @Override
                    public void run() {
                        try {
                            spiltOrder(order.getOrderNo(),isAutoClean);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                };
                //20秒后再执行
                scheduledExecutorService.schedule(task, 100L, TimeUnit.MILLISECONDS);
            }
        });
    }

    /**
     * 分账结果查询
     */
    @Override
    @Transactional
    public void spiltTaskQuery() {
        List<PaySplit> paySplits = iPaySplitService.list(Wrappers.lambdaQuery(PaySplit.class)
            .eq(PaySplit::getState, PaySplitState.ROCESSING.getCode())
        );
        for (PaySplit paySplit : paySplits) {
            Order order = this.getByOrderNo(paySplit.getOrderNo());
            try {
                ProfitSharingResult profitSharingResult = wxPayService.getProfitSharingV3Service().getProfitSharingResult(paySplit.getOutOrderNo(), paySplit.getTransactionNo());
                paySplit.setSpiltTransactionNo(profitSharingResult.getTransactionId());
                if (paySplit.getState().equals(PaySplitState.FINISHED.getCode())) {
                    order.setPaySpiltStatus(OrderPaySpiltStatusEnum.spilt_success.getCode());
                }
                this.updateById(order);
                iPaySplitService.updateById(paySplit);
            } catch (WxPayException e) {
                log.error("微信分账查询失败分账订单号:{}", paySplit.getOutOrderNo(), e);
            }
        }
    }

    /**
     * 订单统计
     */
    @Override
    public void orderStatistics() {
        DateTime yesterday = DateUtil.beginOfDay(DateUtil.yesterday());
        List<Order> list = lambdaQuery().in(Order::getOrderStatus, OrderStatusEnum.IN_USED.getCode(), OrderStatusEnum.USED_DONE.getCode())
            .eq(Order::getIsOffline, "online")
            .apply("create_time BETWEEN {0} and {1}", yesterday, DateUtil.endOfDay(yesterday))
            .list();
        //提取所有的站点ID
        log.info("今日总订单：" + list.size());
        List<Long> positionIds = list.stream().map(Order::getPositionId).distinct().collect(Collectors.toList());
        List<Position> positions = positionService.listByIds(positionIds);
        List<OrderStatistics> statisticsList = new ArrayList<>();
        positions.stream().forEach(position -> {
            List<Order> orderList = list.stream().filter(item -> item.getPositionId().longValue() == position.getPositionId().longValue()).collect(Collectors.toList());
            orderList.stream().map(Order::getDeviceNo).forEach(deviceNo -> {
                OrderStatistics orderStatistics = new OrderStatistics();
                orderStatistics.setPositionId(position.getPositionId());
                orderStatistics.setDeviceNo(deviceNo);
                orderStatistics.setName(position.getName());
                orderStatistics.setDateTag(yesterday);
                BigDecimal statisticsPrice = ObjectUtil.defaultIfNull(position.getStatisticsPrice(), BigDecimal.ZERO);
                orderStatistics.setStatisticsPrice(statisticsPrice);
                //统计当前站点的收益
                Stream<Order> orderStream = orderList.stream().filter(item -> StrUtil.equals(item.getDeviceNo(), deviceNo));
                long count = orderStream
                    .count();
                orderStatistics.setCount(count);
                if (count > 0) {
                    orderStatistics.setIncome(orderStream.map(Order::getActualDeviceAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO));
                    orderStatistics.setStatisticsIncome(NumberUtil.mul(count, statisticsPrice));
                    statisticsList.add(orderStatistics);
                }
            });

        });
        log.info("统计记录条数：" + statisticsList.size());
        orderStatisticsService.saveBatch(statisticsList);
    }

    /**
     * 订单分账
     *
     * @param orderNo
     * @return
     */
    @Override
    @Lock4j(keys = {"#orderNo"}, expire = 3000, acquireTimeout = 5000)
    @Transactional(rollbackFor = Exception.class)
    public boolean spiltOrder(String orderNo,boolean isAutoClean) {
        log.info("开始对订单号:{} 进行分账", orderNo);
        Order order = this.getOne(Wrappers.<Order>lambdaQuery()
            .eq(Order::getOrderNo, orderNo));
        if (order != null && order.getPayWay().equals(OrderPayWayEnum.wx_pay.getCode()) && order.getPaySpiltStatus().equals(OrderPaySpiltStatusEnum.spilt_no.getCode())) {
            Long positionId = order.getPositionId();
            Position position = positionService.getById(positionId);
            if (position != null && StringUtils.isNotBlank(position.getOpenid()) && position.getOpenSpilt().equals(OpenSpiltEnum.open.getCode())) {
                ManagerConfig managerConfig = managerConfigService.getOne(Wrappers.lambdaQuery(ManagerConfig.class)
                    .eq(ManagerConfig::getPositionId, positionId)
                    .last(" Limit 1")
                );
                Long mangerId = null;
                if (managerConfig != null) {
                    mangerId = managerConfig.getManagerId();
                }

                String spiltOrderNo = generateSpiltOrderNo();
                PaySplit paySplit = new PaySplit();
                paySplit.setPositionId(positionId);
                paySplit.setManagerId(mangerId);
                paySplit.setOpenid(position.getOpenid());
                paySplit.setOrderNo(orderNo);
                paySplit.setOutOrderNo(spiltOrderNo);
                paySplit.setTransactionNo(order.getTransactionNo());
                paySplit.setDescription("Fees");
                BigDecimal payAmount = order.getPayAmount();

                BigDecimal orderSpiltRate = BigDecimal.valueOf(ORDER_SPILT_RATE);
                String ORDER_SPILT_RATE = sysConfigService.selectConfigByKey(OssConstant.ORDER_SPILT_RATE);
                if (NumberUtil.isNumber(ORDER_SPILT_RATE)) {
                    BigDecimal DB_SPILT_RATE = new BigDecimal(ORDER_SPILT_RATE);
                    if (DB_SPILT_RATE.compareTo(orderSpiltRate) <= 0) {
                        orderSpiltRate = DB_SPILT_RATE;
                    }
                }

                if (position.getSpiltRate() != null) {
                    if (position.getSpiltRate().compareTo(orderSpiltRate) <= 0) {
                        orderSpiltRate = position.getSpiltRate();
                    }
                }
                paySplit.setOrderAmount(payAmount);
                BigDecimal divide = payAmount.multiply(orderSpiltRate).divide(new BigDecimal("100")).setScale(2);
                paySplit.setSplitAmount(divide);
                paySplit.setState(PaySplitState.ROCESSING.getCode());
                order.setPaySpiltStatus(OrderPaySpiltStatusEnum.spilting.getCode());

                ProfitSharingReceiver.ProfitSharingReceiverBuilder receiverBuilder = ProfitSharingReceiver.newBuilder();
                receiverBuilder
                    .appid(properties.getAppId())
                    .type("PERSONAL_OPENID")
                    .description(paySplit.getDescription())
                    .account(paySplit.getOpenid())
                    .amount(divide.multiply(new BigDecimal("100")).setScale(0).longValue());

                ProfitSharingReceiver receiver = receiverBuilder.build();

                ProfitSharingRequest.ProfitSharingRequestBuilder profitSharingRequestBuilder = ProfitSharingRequest.newBuilder();
                profitSharingRequestBuilder
                    .outOrderNo(paySplit.getOutOrderNo())
                    .appid(properties.getAppId())
                    .transactionId(paySplit.getTransactionNo())
                    .unfreezeUnsplit(true)
                    .receivers(Collections.singletonList(receiver))
                ;

                ProfitSharingRequest request = profitSharingRequestBuilder.build();
                try {
                    ProfitSharingResult profitSharingResult = wxPayService.getProfitSharingV3Service().profitSharing(request);
                    paySplit.setSpiltTransactionNo(profitSharingResult.getTransactionId());
                    paySplit.setState(profitSharingResult.getState());
                } catch (WxPayException e) {
                    log.error("微信分账失败订单号:{}", orderNo, e);
                    throw new RuntimeException(e);
                }

                if (paySplit.getState().equals(PaySplitState.FINISHED.getCode())) {
                    order.setPaySpiltStatus(OrderPaySpiltStatusEnum.spilt_success.getCode());
                }

                iPaySplitService.save(paySplit);
                this.updateById(order);
                orderAutoRefundTask(order,true,isAutoClean);
                return true;
            }
            orderAutoRefundTask(order,false,isAutoClean);
        }
        return false;
    }


    /**
     * 10分钟内结束或者以前下过单要自动退款的设备
     */
    private static List<String> autoRefundDeviceNoList=new ArrayList<String>(){{
       add("1777883442646175744");
       add("1777883735127576576");
       add("1851095190186008576");
        add("1773327870731014144");
        add("1773277882038661120");
        add("1773277508238094336");
        add("1773186538523897856");
        add("1773186205882036224");

    }};

    /**
     * 正常结束订单就自动退款的设备
     */
    private static List<String> autoRefundDeviceNoList2=new ArrayList<String>(){{
//        add("1773327870731014144");
//        add("1773277882038661120");
//        add("1773277508238094336");
//        add("1773186538523897856");
//        add("1773186205882036224");
        add("1870755073286049792");
        add("1871041215436787712");
    }};

    public void orderAutoRefundTask(Order order,boolean isSpiltOrder,boolean isAutoClean){
        //如果是自动清柜，不退款
        if(isAutoClean){
            return;
        }
        String orderNo=order.getOrderNo();
        String deviceNo = order.getDeviceNo();
        if(autoRefundDeviceNoList.contains(deviceNo)){
            if (BigDecimal.ZERO.compareTo(order.getResidueAmount()) == -1) {
                //判断是否10分钟内结束，是的话，自动退款原路退回
                if (DateUtil.between(order.getEndTime(), order.getStartTime(), DateUnit.MINUTE) < 10) {
                    log.info("订单号:{}完成，10分钟内结束，自动退款", orderNo);
                    TimerTask task = new TimerTask() {
                        @Override
                        public void run() {
                            try {
                                //原路退回
                                withdrawService.withdrawBySystem(order.getUserId(), order.getOrderNo(), WithdrawTypeEnum.AUTO_REFUND.getCode());
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    };
                    //开分张 2分钟，没开分账，延迟1秒直接退
                    scheduledExecutorService.schedule(task, isSpiltOrder?120L:1L, TimeUnit.SECONDS);
                } else {
                    //查询用户是否以前在系统中用过，用过的话，当前这笔也原路退回
                    Long count = lambdaQuery().eq(Order::getUserId, order.getUserId()).ne(Order::getOrderId, order.getOrderId()).count();
                    if (count > 0) {
                        log.info("订单号:{}完成，用户之前有消费，自动退款", orderNo);
                        TimerTask task = new TimerTask() {
                            @Override
                            public void run() {
                                try {
                                    //原路退回
                                    withdrawService.withdrawBySystem(order.getUserId(), order.getOrderNo(), WithdrawTypeEnum.AUTO_REFUND.getCode());
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        };
                        //开分张 2分钟，没开分账，延迟1秒直接退
                        scheduledExecutorService.schedule(task, isSpiltOrder?120L:1L, TimeUnit.SECONDS);
                    }
                }
            }
        }else if(autoRefundDeviceNoList2.contains(deviceNo)){
            log.info("订单号:{}完成，订单正常结束，自动退款", orderNo);
            TimerTask task = new TimerTask() {
                @Override
                public void run() {
                    try {
                        //原路退回
                        withdrawService.withdrawBySystem(order.getUserId(), order.getOrderNo(), WithdrawTypeEnum.AUTO_REFUND.getCode());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            //开分张 2分钟，没开分账，延迟1秒直接退
            scheduledExecutorService.schedule(task, isSpiltOrder?120L:1L, TimeUnit.SECONDS);
        }

    }


    /**
     * 计算费用
     *
     * @return
     */
    private BigDecimal calculateFeeByHours(Order order, LocalDateTime endTime) {
        String free_minutes_value = sysConfigService.selectConfigByKey(SYS_FREE_MINUTES_KEY);
        LocalDateTime startTime = DateUtil.toLocalDateTime(order.getStartTime());
        Duration duration = Duration.between(startTime, endTime);
        long minutes = duration.toMinutes();
        minutes = Math.abs(minutes);
        int free_minutes = 10;
        if (StringUtils.isNotEmpty(free_minutes_value)) {
            free_minutes = Integer.parseInt(free_minutes_value);
        }
        if (minutes <= free_minutes) {
            return BigDecimal.ZERO;
        }

        String orderType = order.getOrderType();
        if (OrderTypeEnum.TIMES.getCode().equals(orderType)) {
            return order.getPrestoreDeviceAmount();
        }
        if (OrderTypeEnum.HOUR.getCode().equals(orderType)) {
            Double fee = 0.00;
            int hourlyFee = (int) (minutes / 60) + 1;
            if (hourlyFee > 1) {
                fee = Math.min(order.getPrestoreDeviceAmount().doubleValue(), order.getUnitPrice()
                    .multiply(BigDecimal.valueOf(hourlyFee)).doubleValue());
            }
            return BigDecimal.valueOf(fee);
        }
        return BigDecimal.ZERO;
    }


    /**
     * desc: 生成订单号
     *
     * @return
     */
    private String generateOrderNo() {
        String currentTime = DateUtil.now().replaceAll("[-: ]", "");
        int randomNumber = RandomUtil.randomInt(1000000);
        return currentTime + randomNumber;
    }

    /**
     * 生成分账订单号
     *
     * @return
     */
    private String generateSpiltOrderNo() {
        String currentTime = DateUtil.now().replaceAll("[-: ]", "");
        int randomNumber = RandomUtil.randomInt(100000);
        return "T" + currentTime + randomNumber;
    }

    /**
     * desc: 手机号校验
     *
     * @param phoneNumber
     * @return
     */
    public static boolean isValidPhoneNumber(String phoneNumber) {
        // 使用正则表达式验证手机号格式
        String regex = "^(1[3-9]\\d{9})$";
        return ReUtil.isMatch(regex, phoneNumber);
    }

    @Override
    public Order getByOrderNo(String orderNo) {
        return this.getOne(Wrappers.<Order>lambdaQuery().eq(Order::getOrderNo, orderNo));
    }

    @Override
    public Page<OrderFlowVo> queryOrderFlow(ManagerClientQueryDto queryDto, PageQuery pageQuery) {
        pageQuery.setOrderByColumn(null);
        //处理查询时间
        if (StrUtil.isNotBlank(queryDto.getStartDateTime())) {
            queryDto.setStartDateTime(queryDto.getStartDateTime() + " 00:00:00");
        }
        if (StrUtil.isNotBlank(queryDto.getEndDateTime())) {
            queryDto.setEndDateTime(queryDto.getEndDateTime() + " 23:59:59");
        }
        return baseMapper.queryOrderFlow(pageQuery.build(), queryDto);
    }

    @Override
    public Page<DeviceFlowVo> queryDeviceFlow(ManagerClientQueryDto queryDto, PageQuery pageQuery) {
        pageQuery.setOrderByColumn(null);
        //处理查询时间
        if (StrUtil.isNotBlank(queryDto.getStartDateTime())) {
            queryDto.setStartDateTime(queryDto.getStartDateTime() + " 00:00:00");
        }
        if (StrUtil.isNotBlank(queryDto.getEndDateTime())) {
            queryDto.setEndDateTime(queryDto.getEndDateTime() + " 23:59:59");
        }
        return baseMapper.queryDeviceFlow(pageQuery.build(), queryDto);
    }

    @Override
    public Page<PositionFlowVo> queryPositionFlow(ManagerClientQueryDto queryDto, PageQuery pageQuery) {
        pageQuery.setOrderByColumn(null);
        //处理查询时间
        if (StrUtil.isNotBlank(queryDto.getStartDateTime())) {
            queryDto.setStartDateTime(queryDto.getStartDateTime() + " 00:00:00");
        }
        if (StrUtil.isNotBlank(queryDto.getEndDateTime())) {
            queryDto.setEndDateTime(queryDto.getEndDateTime() + " 23:59:59");
        }
        return baseMapper.queryPositionFlow(pageQuery.build(), queryDto);
    }

    /**
     * 统计查询条件转换
     */
    private void changeStatisticsQueryDto(ManagerClientQueryDto queryDto) {
        //处理查询时间
        if (StrUtil.isNotBlank(queryDto.getStartDateTime())) {
            queryDto.setStartDateTime(queryDto.getStartDateTime() + " 00:00:00");
        }
        if (StrUtil.isNotBlank(queryDto.getEndDateTime())) {
            queryDto.setEndDateTime(queryDto.getEndDateTime() + " 23:59:59");
        }

        //将区域条件转换为场地查询
        String belongingPlaceStr = queryDto.getBelongingPlaceStr();
        if (StrUtil.isBlank(belongingPlaceStr)) {
            return;
        }
        List<Long> positionIdList = new ArrayList<>();

        // 获取符合区域条件的位置列表
        List<Position> list = positionService.list(
            Wrappers.<Position>lambdaQuery().like(Position::getBelongingPlaceStr, belongingPlaceStr));
        if (CollUtil.isEmpty(list)) {
            positionIdList.add(-1L);
        } else {
            positionIdList = list.stream().map(Position::getPositionId).collect(Collectors.toList());
        }
        //获取查询条件的场地ids
        List<Long> idList = queryDto.getPositionIdList();
        if (CollUtil.isNotEmpty(idList)) {
            //不为空则取交集
            idList.retainAll(positionIdList);
            queryDto.setPositionIdList(idList);
        } else {
            //场地条件为空，者取当前的的场地条件
            queryDto.setPositionIdList(positionIdList);
        }
    }

    @Override
    public StatisticsVo statistics(ManagerClientQueryDto queryDto) {
        //处理查询条件
        changeStatisticsQueryDto(queryDto);
        //查询近七天的数据
        List<DailyOrderVo> dailyOrderVos = baseMapper.sevenDaysData(queryDto);
        //查询合计
        DailyOrderVo total = baseMapper.statistics(queryDto);
        StatisticsVo statisticsVo = new StatisticsVo();
        //总收益
        statisticsVo.setTotalAmount(ObjectUtil.isNull(total.getAmount()) ? BigDecimal.ZERO : total.getAmount());
        //未退的押金总金额
        statisticsVo.setUnrefundOrderAmount(ObjectUtil.isNull(total.getUnrefundOrderAmount()) ? BigDecimal.ZERO : total.getUnrefundOrderAmount());
        //总订单量
        statisticsVo.setTotalOrderNumber(ObjectUtil.isNull(total.getOrderNumber()) ? 0 : total.getOrderNumber());
        //未退押金的订单量
        statisticsVo.setUnrefundOrderNumber(ObjectUtil.isNull(total.getUnrefundOrderNumber()) ? 0 : total.getUnrefundOrderNumber());
        statisticsVo.setTjAmount(ObjUtil.defaultIfNull(total.getTjAmount(), BigDecimal.ZERO));
        List<String> sevenDay = getSevenDay();
        for (int i = 0; i < sevenDay.size(); i++) {
            String day = sevenDay.get(i);
            Optional<DailyOrderVo> result = dailyOrderVos.stream().filter(d -> d.getDateLine().equals(day)).findFirst();
            DailyOrderVo dailyOrderVo;
            if (result.isPresent()) {
                dailyOrderVo = result.get();
            } else {
                dailyOrderVo = new DailyOrderVo();
                dailyOrderVo.setDateLine(day);
                dailyOrderVo.setOrderNumber(0);
                dailyOrderVo.setAmount(BigDecimal.ZERO);
                dailyOrderVo.setUnrefundOrderAmount(BigDecimal.ZERO);
                dailyOrderVo.setUnrefundOrderNumber(0);
                dailyOrderVos.add(dailyOrderVo);
            }
            if (i == 0) {
                statisticsVo.setTodayOrderNumber(dailyOrderVo.getOrderNumber());
                statisticsVo.setTodayAmount(dailyOrderVo.getAmount());
                statisticsVo.setTodayTjAmount(dailyOrderVo.getTjAmount());
                statisticsVo.setTodayUnrefundAmount(dailyOrderVo.getUnrefundOrderAmount());
                statisticsVo.setTodayUnrefundOrderNumber(dailyOrderVo.getUnrefundOrderNumber());
            } else if (i == 1) {
                statisticsVo.setYesterdayOrderNumber(dailyOrderVo.getOrderNumber());
                statisticsVo.setYesterdayAmount(dailyOrderVo.getAmount());
            }
        }
        //如果指定时间范围就返回指定范围的数据，否则返回近七天的数据
        if (StrUtil.isNotBlank(queryDto.getStartDateTime()) && StrUtil.isNotBlank(queryDto.getEndDateTime())) {
            List<DailyOrderVo> dailyOrderVos1 = baseMapper.queryByDateRange(queryDto);
            List<String> datesBetween = getDatesBetween(queryDto.getStartDateTime(), queryDto.getEndDateTime());
            for (int i = 0; i < datesBetween.size(); i++) {
                String day = datesBetween.get(i);
                Optional<DailyOrderVo> result = dailyOrderVos1.stream().filter(d -> d.getDateLine().equals(day)).findFirst();
                DailyOrderVo dailyOrderVo;
                if (result.isEmpty()) {
                    dailyOrderVo = new DailyOrderVo();
                    dailyOrderVo.setDateLine(day);
                    dailyOrderVo.setOrderNumber(0);
                    dailyOrderVo.setAmount(BigDecimal.ZERO);
                    dailyOrderVo.setUnrefundOrderAmount(BigDecimal.ZERO);
                    dailyOrderVo.setUnrefundOrderNumber(0);
                    dailyOrderVos1.add(dailyOrderVo);
                }
            }
            statisticsVo.setDailyOrderVoList(dailyOrderVos1);
        } else {
            statisticsVo.setDailyOrderVoList(dailyOrderVos);
        }
        return statisticsVo;
    }

    @Override
    public List<Map<String, Integer>> getOrderNumByDeviceId(ManagerClientQueryDto queryDto) {
        if ("year".equalsIgnoreCase(queryDto.getDateType())) {
            queryDto.setDateFormat("%Y");
        } else if ("month".equalsIgnoreCase(queryDto.getDateType())) {
            queryDto.setDateFormat("%Y-%m");
            LocalDate lastDayOfYear = Year.of(queryDto.getYear()).atMonth(12).atEndOfMonth();
            String format = LocalDateTimeUtil.format(lastDayOfYear, "yyyy-MM-dd");
            //计算月的开始时间和结束时间
            queryDto.setStartDateTime(queryDto.getYear() + "01-01 00:00:00");
            queryDto.setEndDateTime(format + " 23:59:59");
        } else {
            queryDto.setDateFormat("%Y-%m-%d");
            String dateStr = queryDto.getMonth() + "-01";
            int lastDayOfMonth = DateUtil.getLastDayOfMonth(DateUtil.parse(dateStr));
            queryDto.setStartDateTime(queryDto.getMonth() + "-01 00:00:00");
            queryDto.setEndDateTime(queryDto.getMonth() + "-" + lastDayOfMonth + " 23:59:59");
        }
        return baseMapper.getOrderNumByDeviceId(queryDto);
    }

    @Override
    public Page<OrderDetailVo> queryOrderByDay(ManagerClientQueryDto queryDto, PageQuery pageQuery) {
        return baseMapper.queryOrderByDay(pageQuery.build(), queryDto);
    }

    private List<String> getSevenDay() {
        Date currentDate = DateUtil.date();
        List<String> dates = new ArrayList<>();
        for (int i = 0; i < 7; i++) {
            Date date = DateUtil.offsetDay(currentDate, -i);
            String formattedDate = DateUtil.format(date, "yyyy-MM-dd");
            dates.add(formattedDate);
        }
        return dates;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void orderSettlement() {
        log.info("------>开始分润---orderSettlement--->");

        //获取待可分账的订单
        List<Order> list = this.list(Wrappers.<Order>lambdaQuery()
            .eq(Order::getSettlementStatus, OrderSettlementStatusEnum.SETTLEABLE.getCode()));
        if (CollUtil.isEmpty(list)) {
            return;
        }
        Set<Long> positions = list.stream().map(Order::getPositionId).collect(Collectors.toSet());

        //获取场地分润配置
        List<ManagerConfig> managerConfigs = managerConfigService.list(
            Wrappers.<ManagerConfig>lambdaQuery().in(ManagerConfig::getPositionId, positions));
        if (CollUtil.isEmpty(managerConfigs)) {
            return;
        }
        Map<Long, ManagerConfig> configMap = managerConfigs.stream()
            .collect(Collectors.toMap(ManagerConfig::getPositionId, Function.identity()));
        List<DividendRecord> recordList = CollUtil.newArrayList();
        List<String> orderList = CollUtil.newArrayList();

        //用户金额
        Map<String, BigDecimal> userAmountMap = MapUtil.newHashMap();

        //处理分账
        for (Order order : list) {
            String orderNo = order.getOrderNo();
            Long positionId = order.getPositionId();
            ManagerConfig managerConfig = configMap.get(positionId);
            if (ObjectUtil.isNull(managerConfig)) {
                continue;
            }
            DividendRecord record = new DividendRecord();
            //分账金额
            BigDecimal actualDeviceAmount = order.getActualDeviceAmount();
            record.setOrderNo(orderNo);
            record.setPositionId(positionId);
            Long managerId = managerConfig.getManagerId();
            record.setManagerId(managerId);
            Long parentManagerId = managerConfig.getParentManagerId();
            record.setParentManagerId(parentManagerId);
            record.setAmount(actualDeviceAmount);
            //代理商金额
            BigDecimal agentAmount = actualDeviceAmount.multiply(managerConfig.getParentRate())
                .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
            record.setDividendParentAmount(agentAmount);
            setUserAmount(userAmountMap, parentManagerId, agentAmount, positionId);
            //管理员金额
            BigDecimal manageAmount = actualDeviceAmount.multiply(managerConfig.getRate())
                .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
            record.setDividendManagerAmount(manageAmount);
            setUserAmount(userAmountMap, managerId, manageAmount, positionId);
            //平台金额
            record.setDividendPlatformAmount(actualDeviceAmount.subtract(agentAmount).subtract(manageAmount));
            recordList.add(record);

            orderList.add(orderNo);
        }

        dividendRecordService.saveBatch(recordList);
        //将金额添加到对应用户的账户
        for (String aLong : userAmountMap.keySet()) {
            BigDecimal bigDecimal = userAmountMap.get(aLong);
            String[] split = aLong.split("-");
            Long userId = Long.valueOf(split[0]);
            Long positionId = Long.valueOf(split[1]);
            setFees(userId, positionId, bigDecimal);
        }

        this.update(Wrappers.<Order>lambdaUpdate()
            .set(Order::getSettlementStatus, OrderSettlementStatusEnum.SETTLED.getCode())
            .in(Order::getOrderNo, orderList));

    }

    private void setFees(Long userId, Long positionId, BigDecimal amount) {
        ManagerFees fees = feesService.getOne(Wrappers.<ManagerFees>lambdaQuery()
            .eq(ManagerFees::getSysUserId, userId)
            .eq(ManagerFees::getPositionId, positionId));
        if (ObjectUtil.isNull(fees)) {
            ManagerFees fees1 = new ManagerFees();
            fees1.setAmount(amount);
            fees1.setResidueAmount(amount);
            fees1.setFreezeAmount(BigDecimal.ZERO);
            fees1.setSysUserId(userId);
            fees1.setPositionId(positionId);
            feesService.save(fees1);
        } else {

            boolean update = feesService.update(Wrappers.<ManagerFees>lambdaUpdate()
                .set(ManagerFees::getAmount, fees.getAmount().add(amount))
                .set(ManagerFees::getResidueAmount, fees.getResidueAmount().add(amount))
                .eq(ManagerFees::getSysUserId, userId)
                .eq(ManagerFees::getPositionId, positionId)
                .eq(ManagerFees::getVersion, fees.getVersion()));
            if (!update) {
                throw new ServiceException("分账-更新用户账户信息失败!");
            }
        }
    }


    /**
     * 设置用户金额
     *
     * @param map
     * @param userId
     * @param amount
     */
    private void setUserAmount(Map<String, BigDecimal> map, Long userId, BigDecimal amount, Long positionId) {
        if (amount.compareTo(BigDecimal.ZERO) != 1) {
            return;
        }
        String key = StrUtil.join("-", userId, positionId);
        BigDecimal sum = map.getOrDefault(key, BigDecimal.ZERO).add(amount);
        map.put(key, sum);
    }

    /**
     * 超过三天的订单，设置用户余额为0
     */
    @Override
    @Transactional
    public void userOrderOverThreeDay() {
        List<User> users = userService.list(Wrappers.lambdaQuery(User.class)
            .isNotNull(User::getLatestOrderNo)
            .ne(User::getLatestOrderNo, "")
        );
        Date date = new Date();
        for (User user : users) {
            String latestOrderNo = user.getLatestOrderNo();
            if (StringUtils.isNotBlank(latestOrderNo)) {
                Order order = this.getByOrderNo(latestOrderNo);
                if (order != null && order.getPayTime() != null) {
                    Date payTime = order.getPayTime();
                    long between = DateUtil.between(date, payTime, DateUnit.DAY);
                    if (!order.getRefundStatus().equals(OrderRefundStatusEnum.REFUND_SUCCESS.getCode()) && between >= 3L) {
                        userService.update(Wrappers.<User>lambdaUpdate().set(User::getBalanceAmount, BigDecimal.ZERO)
                            .set(User::getLatestOrderNo, "").eq(User::getUserId, user.getUserId()));
                    }
                }
            }
        }
    }


    /**
     * 获取指定范围的每一天
     *
     * @param startDateStr
     * @param endDateStr
     * @return
     */
    public static List<String> getDatesBetween(String startDateStr, String endDateStr) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDate startDate = LocalDate.parse(startDateStr, dateTimeFormatter);
        LocalDate endDate = LocalDate.parse(endDateStr, dateTimeFormatter);
        List<String> collect = Stream.iterate(startDate, date -> date.plusDays(1))
            .limit(ChronoUnit.DAYS.between(startDate, endDate) + 1)
            .map(date -> date.format(formatter))
            .collect(Collectors.toList());
        return CollUtil.reverse(collect);
    }
}
